package com.novel.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.novel.config.RedissonHelper;
import com.novel.entiy.Novel;
import com.novel.entiy.Review;
import com.novel.entiy.RewardRecord;
import com.novel.entiy.dto.LoginDTO;
import com.novel.entiy.dto.RegisterDTO;
import com.novel.entiy.TpUser;
import com.novel.entiy.dto.RewardDTO;
import com.novel.entiy.event.HotEvent;
import com.novel.entiy.vo.UserInfo;
import com.novel.exception.BusinessException;
import com.novel.mapper.NovelMapper;
import com.novel.mapper.ReviewMapper;
import com.novel.mapper.RewardRecordMapper;
import com.novel.mapper.TpUserMapper;
import com.novel.service.NovelService;
import com.novel.service.UserService;
import com.novel.utils.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author tanghao
 * @date 2022/4/5
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private TpUserMapper tpUserMapper;

    @Resource
    private RedissonHelper redissonHelper;

    @Resource
    private JavaMailSenderImpl mailSender;

    @Resource
    private ReviewMapper reviewMapper;

    @Resource
    private NovelMapper novelMapper;

    @Resource
    private NovelService novelService;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Resource
    private RewardRecordMapper rewardRecordMapper;

    @Value("${spring.mail.username}")
    private String from;

    /**
     * 获取当前登陆用户信息
     *
     * @return 当前登录用户信息
     */
    @Override
    public TpUser getUserInfo() {
        HttpServletRequest request = getRequest();
        String token = String.valueOf(request.getSession().getAttribute("token"));
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException("用户未登录");
        }
        TpUser user = (TpUser) redissonHelper.getValue("novel_user" + token);
        if (ObjectUtils.isEmpty(user)) {
            throw new BusinessException("用户未登录");
        }
        return user;
    }

    /**
     * 登录
     * y
     *
     * @param loginDTO 参数
     * @return 用户信息
     */
    @Override
    public UserInfo login(LoginDTO loginDTO) {
        TpUser tpUser = tpUserMapper.selectOne(new QueryWrapper<TpUser>()
                .eq(TpUser.COL_USERNAME, loginDTO.getUsername())
                .or().eq(TpUser.COL_EMAIL, loginDTO.getUsername()));
        if (ObjectUtils.isEmpty(tpUser)) {
            throw new BusinessException("用户名或密码错误");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        if (!bCryptPasswordEncoder.matches(loginDTO.getPassword(), tpUser.getPassword())) {
            throw new BusinessException("用户名或密码错误");
        }
        String token = CommonUtil.randomString(20);
        //缓存中保存用户信息
        redissonHelper.setValueAndTimeUnit("novel_user" + token, tpUser, 12, TimeUnit.HOURS);
        //session中保存token
        HttpServletRequest request = getRequest();
        HttpSession session = request.getSession();
        session.setAttribute("token", token);
        return UserInfo.builder().tpUser(tpUser).token(token).build();
    }

    /**
     * 注册
     *
     * @param registerDTO 参数
     */
    @Override
    public void register(RegisterDTO registerDTO) {
        Object emailCode = redissonHelper.getValue("email_code" + registerDTO.getEmail());
        if (ObjectUtils.isEmpty(emailCode)) {
            throw new BusinessException("邮箱验证码已过期");
        }
        if (!registerDTO.getCode().equals(String.valueOf(emailCode))) {
            throw new BusinessException("邮箱验证码不正确");
        }
        TpUser tpUser = tpUserMapper.selectOne(new QueryWrapper<TpUser>()
                .eq(TpUser.COL_USERNAME, registerDTO.getUsername()));
        if (ObjectUtils.isNotEmpty(tpUser)) {
            throw new BusinessException("用户名已存在");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        TpUser user = TpUser.builder()
                .email(registerDTO.getEmail())
                .password(bCryptPasswordEncoder.encode(registerDTO.getPassword()))
                .username(registerDTO.getUsername())
                .nickname(registerDTO.getUsername())
                .avatar("https://novel-chihuo.oss-cn-beijing.aliyuncs.com/a.jpg")
                .role(0)
                .novelNums(0L)
                .novelWordsNums(0L)
                .goldCoinNums(0L)
                .isSign(0)
                .build();
        tpUserMapper.insert(user);
        redissonHelper.clearBucket("email_code" + registerDTO.getEmail());
    }

    /**
     * 获取邮箱验证码
     *
     * @param email 邮箱
     */
    @Override
    public void getEmailCode(String email) {
        TpUser tpUser = tpUserMapper.selectOne(new QueryWrapper<TpUser>()
                .eq(TpUser.COL_EMAIL, email));
        if (ObjectUtils.isNotEmpty(tpUser)) {
            throw new BusinessException("邮箱已注册");
        }

        String code = CommonUtil.randomString(6);
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("注册验证码");
        message.setText("您的验证码是：" + code + ",有效时间10分钟");
        message.setFrom(from);
        message.setTo(email);
        redissonHelper.setValueAndTimeUnit("email_code" + email, code, 10, TimeUnit.MINUTES);
        mailSender.send(message);
    }


    /**
     * 充值
     *
     * @param amount 数目
     */
    @Override
    public void recharge(Integer amount) {
        TpUser userInfo = getUserInfo();
        userInfo.setGoldCoinNums(userInfo.getGoldCoinNums() + amount * 100);
        updateRedisUser(userInfo);
        tpUserMapper.updateById(userInfo);
        updateRedisUser(userInfo);
    }

    /**
     * 获取HttpServletRequest
     */
    public HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(
                RequestContextHolder.getRequestAttributes())).getRequest();
    }


    /**
     * 更新用户缓存
     *
     * @param tpUser 用户信息
     */
    @Override
    public void updateRedisUser(TpUser tpUser) {
        HttpServletRequest request = getRequest();
        String token = String.valueOf(request.getSession().getAttribute("token"));
        //先清除所有该用户的token;
        redissonHelper.setValueAndTimeUnit("novel_user" + token, tpUser, 12, TimeUnit.HOURS);
    }

    /**
     * 退出登录
     */
    @Override
    public void logout() {
        HttpServletRequest request = getRequest();
        String token = String.valueOf(request.getSession().getAttribute("token"));
        redissonHelper.clearBucket("novel_user" + token);
    }

    /**
     * 申请成为作者
     */
    @Override
    public void applyAuthor() {
        TpUser userInfo = getUserInfo();
        Review build = Review.builder().mainId(userInfo.getId())
                .reviewType(0)
                .createdId(userInfo.getId())
                .createdUser(userInfo.getNickname())
                .createdEmail(userInfo.getEmail())
                .reviewResult(0)
                .mainTitle("作者申请")
                .build();
        reviewMapper.insert(build);
    }

    /**
     * 编辑用户
     *
     * @param tpUser 用户信息
     */
    @Override
    public void editUser(TpUser tpUser) {
        tpUserMapper.updateById(tpUser);
        TpUser tpUser1 = tpUserMapper.selectById(tpUser.getId());
        List<Novel> novels = novelMapper.selectList(new QueryWrapper<Novel>().eq(Novel.COL_AUTHOR_ID, tpUser.getId()));
        if(CollectionUtils.isNotEmpty(novels)){
            novels.forEach(novel -> novel.setAuthorName(tpUser.getNickname()));
            novelMapper.updateBatch(novels);
        }
        updateRedisUser(tpUser1);
    }

    /**
     * 申请签约
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applySign() {
        TpUser userInfo = getUserInfo();
        if (!userInfo.getIsSign().equals(0)) {
            throw new BusinessException("用户状态有误,请稍后再试");
        }
        userInfo.setIsSign(2);
        tpUserMapper.updateById(userInfo);
        updateRedisUser(userInfo);
        List<Novel> novelList = novelMapper.selectList(new QueryWrapper<Novel>().eq(Novel.COL_AUTHOR_ID, userInfo.getId()));
        if (CollectionUtils.isEmpty(novelList)) {
            throw new BusinessException("必须有作品才有资格签约");
        }
        Review build = Review.builder()
                .mainId(userInfo.getId())
                .reviewType(3)
                .createdId(userInfo.getId())
                .createdUser(userInfo.getNickname())
                .createdEmail(userInfo.getEmail())
                .reviewResult(0)
                .build();
        reviewMapper.insert(build);
    }


    /**
     * 打赏小说
     *
     * @param rewardDTO 参数
     */
    @Override
    public void reward(RewardDTO rewardDTO) {
        TpUser userInfo = getUserInfo();
        Long goldCoinNums = userInfo.getGoldCoinNums();
        if (goldCoinNums < rewardDTO.getAmount()) {
            throw new BusinessException("你的余额不足，请充值");
        }
        //打赏人余额扣减
        novelService.charge(userInfo.getId(), -rewardDTO.getAmount());
        //被打赏人余额加
        novelService.charge(rewardDTO.getAuthorId(), rewardDTO.getAmount());
        Novel novel = novelMapper.selectById(rewardDTO.getNovelId());
        //记录打赏记录
        RewardRecord rewardRecord = RewardRecord.builder()
                .amount(rewardDTO.getAmount())
                .novelId(novel.getId())
                .novelTitle(novel.getTitle())
                .authorId(novel.getAuthorId())
                .authorName(novel.getAuthorName())
                .payTime(new Date())
                .userId(userInfo.getId())
                .build();
        rewardRecordMapper.insert(rewardRecord);

        //为作品增加热度
        applicationEventPublisher.publishEvent(HotEvent.builder().novelId(rewardDTO.getNovelId())
                .hotNums(100 * rewardDTO.getAmount()).build());
    }


}
